package cn.zyjblogs.starter.rabbitmq.autoconfig;

import cn.zyjblogs.starter.common.autoconfigure.rsa.RsaKeyProperties;
import cn.zyjblogs.starter.common.entity.constant.CommonRedisKeyConstant;
import cn.zyjblogs.starter.common.entity.constant.ContextKeyConstant;
import cn.zyjblogs.starter.common.entity.constant.HttpHeaderConstant;
import cn.zyjblogs.starter.common.entity.context.BaseContext;
import cn.zyjblogs.starter.common.entity.dto.ContextDto;
import cn.zyjblogs.starter.common.utils.jwt.JwtParsers;
import cn.zyjblogs.starter.common.utils.rsa.RsaUtils;
import cn.zyjblogs.starter.redis.utils.RedisTemplateHandler;
import io.jsonwebtoken.Claims;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.amqp.rabbit.config.SimpleRabbitListenerContainerFactory;
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.amqp.rabbit.listener.RabbitListenerContainerFactory;
import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter;
import org.springframework.boot.autoconfigure.amqp.RabbitTemplateConfigurer;
import org.springframework.boot.autoconfigure.amqp.SimpleRabbitListenerContainerFactoryConfigurer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Paths;
import java.security.PublicKey;
import java.util.Map;
import java.util.Objects;

/**
 * @author zhuyijun
 */
@Configuration
@Slf4j
@RequiredArgsConstructor
public class RabbitMqAutoConfiguration {
    private final RsaKeyProperties rsaKeyProperties;
    private final RedisTemplateHandler redisTemplateHandler;


    @Bean
    public RabbitListenerContainerFactory rabbitListenerContainerFactory(SimpleRabbitListenerContainerFactoryConfigurer configurer, ConnectionFactory connectionFactory) {
        SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory();
        configurer.configure(factory, connectionFactory);
        factory.setMessageConverter(new Jackson2JsonMessageConverter());
        factory.setAfterReceivePostProcessors(message -> {
            Map<String, Object> headers = message.getMessageProperties().getHeaders();
            String authorization = null;
            if (headers.containsKey(HttpHeaderConstant.AUTHORIZATION)) {
                authorization = (String) headers.get(HttpHeaderConstant.AUTHORIZATION);
            }
            if (StringUtils.isEmpty(authorization)) {
                log.warn("rabbitmq组件接收消息时没有获取到token");
                return message;
            }
            setContext(authorization);
            return message;
        });
        return factory;
    }

    @Bean
    public RabbitTemplate rabbitTemplate(RabbitTemplateConfigurer configurer, ConnectionFactory connectionFactory) {
        RabbitTemplate template = new RabbitTemplate();
        configurer.configure(template, connectionFactory);
        template.setMessageConverter(new Jackson2JsonMessageConverter());
        template.setBeforePublishPostProcessors(message -> {
            message.getMessageProperties().getHeaders().put(HttpHeaderConstant.AUTHORIZATION, BaseContext.getToken());
            return message;
        });
        return template;
    }

    /**
     * 设置上下文
     *
     * @param authorization 认证信息
     * @author tanyuanzhi
     * @date 2021/11/12 9:17
     */
    private void setContext(String authorization) {
        if (!authorization.toLowerCase().startsWith(HttpHeaderConstant.BEARER_TYPE.toLowerCase())) {
            return;
        }
        String token = authorization.substring(HttpHeaderConstant.BEARER_TYPE.length()).trim();
        JwtParsers jwtParsers = new JwtParsers(false);

        try {
            if (Boolean.TRUE.equals(rsaKeyProperties.getEnable())) {
                String publicKeyStr = null;
                Object o = redisTemplateHandler.get(CommonRedisKeyConstant.REDIS_KEY_PUBLIC_RSA);
                if (Objects.isNull(o)) {
                    publicKeyStr = IOUtils.toString(Paths.get(rsaKeyProperties.getPubKeyPath()).toUri(), StandardCharsets.UTF_8);
                } else {
                    publicKeyStr = o.toString();
                }
                PublicKey publicKey = RsaUtils.getPublicKeyByKeyStr(publicKeyStr);
                jwtParsers.setSigningKey(publicKey);
                Claims body = jwtParsers.parseClaimsJws(token).getBody();
                String userId = body.get(ContextKeyConstant.USER_ID_KEY, String.class);
                String username = body.get(ContextKeyConstant.USERNAME_KEY, String.class);
                String tenantId = body.get(ContextKeyConstant.TENANT_ID_KEY, String.class);
                if (StringUtils.isEmpty(userId) || StringUtils.isEmpty(tenantId) || StringUtils.isEmpty(username)) {
                    log.error("无效的token:{}", token);
                    return;
                }
                BaseContext.set(ContextDto.builder().userId(userId).username(username).token(token).tenantId(tenantId).build());
                log.debug("rabbitmq: 设置上下文成功");
            }
        } catch (IOException e) {
            log.error("rabbitmq:{}", "token解析失败，token无效");
        }

    }

}
